package db

import (
	"bufio"
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	logger "github.com/sirupsen/logrus"
	"gopkg.in/yaml.v3"
	"io"
	"log"
	"strings"
)

var Connection *DbConnect
var Partial_UFT_MB4 = "characterEncoding=utf8mb4"
var Partial_UFT_8 = "characterEncoding=utf8"
var Partial_UTF_JOIN_8 = "characterEncoding=utf-8"

type DbConnect struct {
	Db         *gorm.DB
	Services   []string
	DbSupports []string
	Tracer     map[string][]*ConfigInfo
	Env        string
	PartialNew string
}

func OpenDb(config *Config) *DbConnect {
	dbConfig := config.DbConnect
	connectUrl := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", dbConfig.User, dbConfig.Password, dbConfig.Url, dbConfig.Db)
	dataSource, err := gorm.Open("mysql", connectUrl)
	if err != nil {
		panic(err)
	}
	dataSource.DB().SetMaxIdleConns(10)
	dataSource.DB().SetMaxOpenConns(100)
	dataSource.SingularTable(true)
	if err != nil {
		panic("failed to connect database")
	}

	Connection = &DbConnect{Db: dataSource, DbSupports: config.DbSupports, Tracer: map[string][]*ConfigInfo{}, Services: config.Services, Env: config.Env, PartialNew: "characterEncoding=utf8mb4"}
	return Connection
}

func (proxy *DbConnect) serviceListConfig(service string) []*ConfigInfo {
	var result []*ConfigInfo
	proxy.Db.Raw("SELECT a.id,a.data_id,a.content,a.md5,a.app_name,a.gmt_modified FROM config_info a inner join  t_service_config c on c.config_id = a.id inner join t_maya_service b on b.id = c.service_id where b.service_name = ?", service).Scan(&result)
	return result
}
func (proxy *DbConnect) supportUri(content string) bool {
	for _, mysqlConnectUri := range proxy.DbSupports {
		if strings.Contains(content, mysqlConnectUri) {
			return true
		}
	}
	return false
}

func (proxy *DbConnect) serviceOwnConfigs(service string, configs []*ConfigInfo) {
	defer func() {
		if err := recover(); err != nil {
			logger.Errorf("%s 更改config %s ", service, err)
		}
	}()
	for _, config := range configs {
		content := proxy.textParser(config)
		if config.IsUpdate && len(content) > 0 {
			logger.Infof("服务:%s,配置文件:%s,更新前内容:%s", service, config.DataID, config.Content)
			config.Content = content
			config.MD5 = md5Hex(config.Content)
			logger.Infof("服务:%s,配置文件:%s,更新后内容:%s", service, config.DataID, config.Content)
		}
	}
}

func (proxy *DbConnect) yamlParser(config *ConfigInfo) {
	var data interface{}
	err := yaml.Unmarshal([]byte(config.Content), &data)
	if err != nil {
		log.Fatal(err)
		logger.Fatalf("%s 不支持yaml数据格式,错误 %s", config.Content, err)
	}
	scanMap, OK := data.(map[string]interface{})
	if OK {
		proxy.recursion(scanMap, config)
	}
	result, err := yaml.Marshal(&data)
	if err != nil {
		log.Fatal(err)
		logger.Fatalf("%s 不支持yaml数据格式,错误 %s", config.Content, err)
	}
	config.Content = string(result)
}

func (proxy *DbConnect) textParser(config *ConfigInfo) string {
	br := bufio.NewReader(strings.NewReader(config.Content))
	var out bytes.Buffer
	output := bufio.NewWriter(&out)
	for {
		wordLine, _, err := br.ReadLine()
		if err == io.EOF {
			break
		}
		line := string(wordLine)
		if proxy.supportUri(line) {
			if strings.Index(line, Partial_UFT_8) > 0 && strings.Index(line, proxy.PartialNew) < 0 {
				line = strings.ReplaceAll(line, Partial_UFT_8, proxy.PartialNew)
				config.IsUpdate = true
			}
			if strings.Index(line, Partial_UTF_JOIN_8) > 0 && strings.Index(line, proxy.PartialNew) < 0 {
				line = strings.ReplaceAll(line, Partial_UFT_8, proxy.PartialNew)
				config.IsUpdate = true
			}
		}
		_, err = output.WriteString(line + "\n")
		if err != nil {
			logger.Fatalf("%s:写入文本 line:%s 错误 %s", config.AppName, line, err)
			return ""
		}
	}
	if config.IsUpdate {
		output.Flush()
		return out.String()
	}
	return config.Content
}

func (proxy *DbConnect) textMb4Parser(config *ConfigInfo) string {
	br := bufio.NewReader(strings.NewReader(config.Content))
	var out bytes.Buffer
	output := bufio.NewWriter(&out)
	for {
		wordLine, _, err := br.ReadLine()
		if err == io.EOF {
			break
		}
		line := string(wordLine)
		if proxy.supportUri(line) {
			if strings.Index(line, Partial_UFT_MB4) > 0 {
				line = strings.ReplaceAll(line, Partial_UFT_MB4, Partial_UFT_8)
				config.IsUpdate = true
			}
		}
		_, err = output.WriteString(line + "\n")
		if err != nil {
			logger.Fatalf("%s:写入文本 line:%s 错误 %s", config.AppName, line, err)
			return ""
		}
	}
	if config.IsUpdate {
		output.Flush()
		return out.String()
	}
	return config.Content
}
func md5Hex(content string) string {
	hash := md5.New()
	hash.Write([]byte(content))
	hashValue := hash.Sum(nil)
	return hex.EncodeToString(hashValue)
}
func (proxy *DbConnect) recursion(findMap map[string]interface{}, config *ConfigInfo) {
	for key, value := range findMap {
		destMap, ok := value.(map[string]interface{})
		if ok {
			proxy.recursion(destMap, config)
		}
		dest, ok := value.(string)
		if ok {
			if proxy.supportUri(dest) {
				if strings.Index(dest, Partial_UFT_8) > 0 && strings.Index(dest, proxy.PartialNew) < 0 {
					dest = strings.ReplaceAll(dest, Partial_UFT_8, proxy.PartialNew)
					config.IsUpdate = true
				}
				if strings.Index(dest, Partial_UTF_JOIN_8) > 0 && strings.Index(dest, proxy.PartialNew) < 0 {
					dest = strings.ReplaceAll(dest, Partial_UFT_8, proxy.PartialNew)
					config.IsUpdate = true
				}
				findMap[key] = dest
				config.IsUpdate = true
			}
		}
	}
}

func (proxy *DbConnect) ReplaceAllService() {
	for _, service := range proxy.Services {
		configInfoList := proxy.serviceListConfig(service)
		if len(configInfoList) == 0 {
			continue
		}
		proxy.serviceOwnConfigs(service, configInfoList)
		proxy.Tracer[service] = configInfoList
	}

	for service, configs := range proxy.Tracer {
		for _, config := range configs {
			if config.IsUpdate {
				proxy.Db.Model(&config).Where("id = ?", config.ID).Updates(map[string]interface{}{"content": config.Content, "MD5": config.MD5})
				logger.Infof("服务:%s,配置文件:%s,更新完毕", service, config.DataID)
			}
		}
	}
}

func (proxy *DbConnect) FallbackAllService() {
	for _, service := range proxy.Services {
		configInfoList := proxy.serviceListConfig(service)
		if len(configInfoList) == 0 {
			continue
		}
		proxy.serviceOwnBackConfigs(service, configInfoList)
		proxy.Tracer[service] = configInfoList
	}

	for service, configs := range proxy.Tracer {
		for _, config := range configs {
			if config.IsUpdate {
				proxy.Db.Model(&config).Where("id = ?", config.ID).Updates(map[string]interface{}{"content": config.Content, "MD5": config.MD5})
				logger.Infof("服务:%s,配置文件:%s,更新完毕", service, config.DataID)
			}
		}
	}
}

func (proxy *DbConnect) serviceOwnBackConfigs(service string, configs []*ConfigInfo) {
	defer func() {
		if err := recover(); err != nil {
			logger.Errorf("%s 更改config %s ", service, err)
		}
	}()
	for _, config := range configs {
		content := proxy.textMb4Parser(config)
		if config.IsUpdate && len(content) > 0 {
			logger.Infof("服务:%s,配置文件:%s,更新前内容:%s", service, config.DataID, config.Content)
			config.Content = content
			config.MD5 = md5Hex(config.Content)
			logger.Infof("服务:%s,配置文件:%s,更新后内容:%s", service, config.DataID, config.Content)
		}
	}
}
